#pragma once

#include <boost/uuid/uuid.hpp>
#include <boostext/typedefs.hpp>
#include <boostext/util/make_shared.hpp>
#include <boostext/iostreams/bin_iostream.hpp>

#include "exports.hpp"




namespace Engine
{
	namespace Debugger
	{
		/**
		 * This exception is thrown when a message cannot be restored because there's not enough
		 * data in the stream.
		 */
		class ENGINE_DEBUGGER_API not_enough_data
			: public boostext::exception
		{
		public:

			not_enough_data()
				: boostext::exception("There's not enough data in the stream to restore a message from it")
			{}
		};
		///////////////////////////////////////////////////////////////////////////////////////////




		/**
		 * A message defines the base class for anything that must be serialized and transported
		 * to some location.
		 *
		 * Currently, there are 2 important implementations: Command and Response.
		 */
		class ENGINE_DEBUGGER_API Message
		{
		public:

			SHARED(Message);

			/**
			 * Defines all available kinds of messages
			 */
			enum Type
			{
				//
				// Commands
				//

				// Controll commands
				Attach,
				Continue,
				Pause,
				Stop,
				Detach,

				// Data storage commands
				SetBreakpoint,

				// Data retrieval commands
				GetScriptState,
				GetCallstack,
				GetVariable,


				//
				// Responses
				//

				// Simple void response, ie the command has been executed
				Response,

				// Notifcations that carry data (ie. return values)
				ReturnScriptState,
				ReturnCallstack,

				MaxMessages = 255,
			};



			/**
			 * Creates an empty message.
			 */
			Message();

			/**
			 * Creates a message with the given tag.
			 * It is up to the implementation to choose an appropriate tag.
			 */
			Message(boost::uuids::uuid tag);
			virtual ~Message();



			/**
			 * @returns the tag that uniquely identifies each Command instance
			 */
			boost::uuids::uuid tag() const;

			/**
			 * @returns the type of this command
			 */
			virtual Type type() const = 0;

			/**
			 * Serializes this command into the given stream.
			 */
			virtual void store(boostext::iostreams::bin_ostream& stream) const;

			/**
			 * Deserializes this command from the given stream.
			 *
			 * @throws exception when the command cannot be restored from the stream
			 */
			virtual void restore(boostext::iostreams::bin_istream& stream);

		private:

			/// Unique Tag that identifies each messsage instance
			boost::uuids::uuid   m_tag;

		};
		///////////////////////////////////////////////////////////////////////////////////////////

		ENGINE_DEBUGGER_API boostext::iostreams::bin_ostream& operator<<(boostext::iostreams::bin_ostream& stream, const Message& message);
		ENGINE_DEBUGGER_API boostext::iostreams::bin_ostream& operator<<(boostext::iostreams::bin_ostream& stream, Message::const_ptr message);

		/**
		 * This function takes care of restoring an instance of Message from the stream.
		 * This can only work if the message has been serialized using operator<<(std::ostream, const Message&).
		 *
		 * @throws not_enought_data          when there's not enough data in the stream to restore a message
		 * @throws boostext::ios_exception   when there's something wrong (probably related to a bug in the serialization functions)
		 *                                   or related to data corruption
		 */
		ENGINE_DEBUGGER_API boostext::iostreams::bin_istream& operator>>(boostext::iostreams::bin_istream& stream, Message::ptr& message);
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
